package com.android.lovely.ui.chat

import androidx.lifecycle.viewModelScope
import com.android.lovely.api.UnlockCardUseCase
import com.android.lovely.api.collectRobotUseCase
import com.android.lovely.api.getChatHistoryUseCase
import com.android.lovely.api.manager.netWorkChangeState
import com.android.lovely.api.queryNewMessageCount
import com.android.lovely.api.service.botService
import com.android.lovely.api.service.systemService
import com.android.lovely.api.service.userService
import com.android.lovely.const.CardUL
import com.android.lovely.const.ChatChange
import com.android.lovely.data.HistoryChat
import com.android.lovely.data.HomeListData
import com.android.lovely.framework.BaseViewModel
import com.android.lovely.framework.UIController.showToast
import com.android.lovely.framework.doContinuousOperation
import com.android.lovely.framework.launchWithLoading
import com.android.lovely.manager.KVStore
import com.android.lovely.room.BotCacheService
import com.android.lovely.room.BotCacheService.observeBot
import com.android.lovely.room.chat.ChatHelper
import com.android.lovely.room.chat.ChatProfileData
import com.android.lovely.room.chat.ConversationStream
import com.android.lovely.room.chat.database.BaseMessage
import com.android.lovely.room.chat.database.CacheMessage
import com.android.lovely.room.chat.database.ChatDatabase
import com.android.lovely.room.chat.database.ChatDataBean
import com.android.lovely.room.chat.database.ImageMessage
import com.android.lovely.room.chat.database.LoadMoreMessage
import com.android.lovely.room.chat.database.LoadingMessage
import com.android.lovely.room.chat.database.ReceivedTextMessage
import com.android.lovely.room.chat.database.SendTextMessage
import com.android.lovely.user.UserManager
import com.kotlin.extensions.bus.FlowBus
import com.kotlin.extensions.bus.post
import com.kotlin.extensions.coroutine.CoroutineExt.launchSafety
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import java.util.UUID
import kotlin.math.abs

/**
 *
 * @author why
 * @date 2025/3/27 15:21
 */

/**
 * 聊天事件类型，用于统一处理所有聊天事件
 */
sealed class ConversationEvent {
    data class MessageReceived(val message: BaseMessage) : ConversationEvent()
    data class MessagesReceived(val messages: List<BaseMessage>) : ConversationEvent()
    data class MessageDeleted(val message: BaseMessage) : ConversationEvent()
    data class HistoryLoaded(val messages: List<BaseMessage>) : ConversationEvent()
    data object ResetMessages : ConversationEvent()
}

/**
 * 会话视图模型，负责处理机器人聊天相关的所有逻辑
 *
 * @property botId Int 机器人ID
 */
class ConversationViewModel(val botId: Int) : BaseViewModel() {

    private val _eventFlow: MutableSharedFlow<ConversationEvent> = MutableSharedFlow(
        replay = 0,
        extraBufferCapacity = 100,
        onBufferOverflow = BufferOverflow.DROP_OLDEST
    )
    val eventFlow: SharedFlow<ConversationEvent> get() = _eventFlow
    
    private val _infoChangedFlow: MutableSharedFlow<ChatProfileData> = MutableSharedFlow(replay = 1)
    val infoChangedFlow: SharedFlow<ChatProfileData> get() = _infoChangedFlow

    val recommendList: ArrayList<HomeListData> = arrayListOf()


    var isSending: Boolean = false
        private set

    val character: ChatProfileData = ChatProfileData()

    var autoTranslate: Boolean = KVStore.talkTranslate
        set(value) {
            field = value
            KVStore.talkTranslate = value
        }

    var chatMode: Int = fetchStoredMode()
        set(value) {
            field = value
            persistMode(value)
        }
    

    var tempMode: Int = 0

    private fun fetchStoredMode(): Int {
        return ChatDatabase.getInstance().getModeDao().get(botId.toString())?.mode ?: 0
    }

    private fun persistMode(mode: Int) {
        viewModelScope.launchSafety {
            ChatDatabase.getInstance().getModeDao().put(
                ChatDataBean(botId.toString(), mode)
            )
        }
    }

    fun updateSendingState(sending: Boolean) {
        isSending = sending
    }


    private fun fetchHistoryMessages() {
        viewModelScope.launchSafety {
            doContinuousOperation {
                val storedMessages = getStoredMessages()
                val messageHistory = getMessageHistory(storedMessages)
                var hasMoreHistory = checkMoreHistoryAvailable(messageHistory)
                val displayMessages = convertToDisplayMessages(messageHistory)
                if(displayMessages.isEmpty()) {
                    hasMoreHistory = false
                }
                if (hasMoreHistory) {
                    addLoadMoreMessage(displayMessages)
                }
                emitHistoryLoadedEvent(displayMessages)
                if (storedMessages.isNotEmpty()) {
                    retrieveNewMessages(storedMessages)
                }
            }
        }
    }

    private suspend fun getStoredMessages(): List<CacheMessage> {
        return ChatHelper.getMessages(botId, Int.MAX_VALUE, 10)
    }

    private suspend fun getMessageHistory(storedMessages: List<CacheMessage>): List<CacheMessage> {
        return storedMessages.ifEmpty {
            getChatHistoryUseCase(botId).map { it }.apply {
                ChatHelper.insertMessages(this)
            }
        }
    }

    private fun checkMoreHistoryAvailable(messageHistory: List<CacheMessage>): Boolean {
        return messageHistory.isNotEmpty() && messageHistory.last().id > 0
    }

    private fun convertToDisplayMessages(messageHistory: List<CacheMessage>): MutableList<BaseMessage> {
        val displayMessages = mutableListOf<BaseMessage>()
        messageHistory.forEach { entry ->
            entry.getReplyMessage()?.also { displayMessages.add(it) }
            entry.getSendMessage()?.also { displayMessages.add(it) }
        }
        return displayMessages
    }

    private fun addLoadMoreMessage(displayMessages: MutableList<BaseMessage>) {
        displayMessages.add(LoadMoreMessage)
    }

    private fun emitHistoryLoadedEvent(displayMessages: MutableList<BaseMessage>) {
        _eventFlow.tryEmit(ConversationEvent.HistoryLoaded(displayMessages))
    }


    private suspend fun retrieveNewMessages(storedMessages: List<CacheMessage>) {
        val newMessagesCount: Int = queryNewMessageCount(botId, storedMessages.first().id)
        if (newMessagesCount > 0) {
            val freshMessages: List<CacheMessage> = getChatHistoryUseCase(botId, size = newMessagesCount)
                .map { it }
                .apply {
                    ChatHelper.insertMessages(this)
                }
            if (freshMessages.isNotEmpty()) {
                val displayMessages: MutableList<BaseMessage> = mutableListOf()
                freshMessages.reversed().forEach { entry ->
                    entry.getSendMessage()?.also { displayMessages.add(0, it) }
                    entry.getReplyMessage()?.also { displayMessages.add(0, it) }
                }
                _eventFlow.emit(ConversationEvent.MessagesReceived(displayMessages))
            }
        }
    }

    suspend fun fetchMoreHistory(messageId: Int): Pair<Boolean, List<BaseMessage>> {
        val olderMessages: List<CacheMessage> = ChatHelper.getMessages(botId, messageId, 10).ifEmpty {
            getChatHistoryUseCase(botId, messageId).map { it }.apply {
                ChatHelper.insertMessages(this)
            }
        }
        var hasMoreHistory: Boolean = olderMessages.isNotEmpty() && olderMessages.last().id > 0
        val displayMessages: MutableList<BaseMessage> = mutableListOf()
        olderMessages.forEach { entry ->
            entry.getReplyMessage()?.also { displayMessages.add(it) }
            entry.getSendMessage()?.also { displayMessages.add(it) }
        }
        if(displayMessages.isEmpty()) {
            hasMoreHistory = false
        }
        return hasMoreHistory to displayMessages
    }

    private fun fetchFeatureInfo() {
        viewModelScope.launchSafety {
            val featureData = systemService.getPayList()
            character.product = featureData.firstPay
            _infoChangedFlow.emit(character)
        }
    }

    private fun fetchAgentInfo() {
        viewModelScope.observeBot(botId) { agentData ->
            character.photoEnable = agentData.canGetImage
            character.scenario = agentData.scenario
            character.info = agentData
            character.intimacyValue = agentData.intimacyValue
            character.chatModel = agentData.hasChatMode
            character.chatPrice = agentData.point
            _infoChangedFlow.emit(character)
        }
    }

    private fun initializeSession() {
        viewModelScope.launchSafety {
            doContinuousOperation {
                val freeMessagesCount: Int = botService.enter(mapOf("bot_id" to botId))["free_num"] ?: 0
                character.freeNum = freeMessagesCount
                _infoChangedFlow.emit(character)
            }
        }
    }

    fun clearConversation() {
        viewModelScope.launchWithLoading {
            botService.reset(mapOf("bot_id" to botId))
            ChatHelper.clearMessage(botId)
            _eventFlow.emit(ConversationEvent.ResetMessages)
        }
    }


    private fun syncConversationState(receivedMsg: ReceivedTextMessage, sendMessage: BaseMessage? = null) {
        viewModelScope.launchSafety {
            val sessionData = fetchSessionData()
            updateBotCache(sessionData)
            updateCharacterInfo(sessionData)
            emitInfoChangedEvent()
            updateUserGems(sessionData)
            updateReceivedMessage(receivedMsg, sessionData)
            insertMessageList(sessionData)
            emitReceivedMessageEvent(receivedMsg)
            updateSendMessageId(sendMessage, sessionData)
            processMessageList(sessionData)
        }
    }

    private suspend fun fetchSessionData(): HistoryChat {
        return botService.getLast(mapOf("bot_id" to botId))
    }

    private fun updateBotCache(sessionData: HistoryChat) {
        BotCacheService.updateBot(botId) {
            intimacyValue = sessionData.intimacyValue
            canGetImage = sessionData.canGetImage
        }
    }

    private fun updateCharacterInfo(sessionData: HistoryChat) {
        character.intimacyValue = sessionData.intimacyValue
        character.freeNum = sessionData.freeNum
        character.photoEnable = sessionData.canGetImage
    }

    private suspend fun emitInfoChangedEvent() {
        _infoChangedFlow.emit(character)
    }

    private fun updateUserGems(sessionData: HistoryChat) {
        UserManager.updateGems(sessionData.surplus)
    }

    private fun updateReceivedMessage(receivedMsg: ReceivedTextMessage, sessionData: HistoryChat) {
        receivedMsg.id = sessionData.info.id
        receivedMsg.showEdit = true
        ChatHelper.insertMessage(sessionData.info.apply { botId = this@ConversationViewModel.botId })
    }

    private fun insertMessageList(sessionData: HistoryChat) {
        ChatHelper.insertMessages(sessionData.messageList.onEach { it.botId = botId })
    }

    private suspend fun emitReceivedMessageEvent(receivedMsg: ReceivedTextMessage) {
        _eventFlow.emit(ConversationEvent.MessageReceived(receivedMsg))
    }

    private fun updateSendMessageId(sendMessage: BaseMessage?, sessionData: HistoryChat) {
        sendMessage?.id = sessionData.info.id
    }

    private suspend fun processMessageList(sessionData: HistoryChat) {
        sessionData.messageList.forEach { entry ->
            entry.getReplyMessage()?.run {
                _eventFlow.emit(ConversationEvent.MessageReceived(this))
                if (this is ImageMessage) {
                    if (unlock) {
                        BotCacheService.updateBot(botId) {
                            cards.find { it.id == cardId }?.isUnlock = true
                        }
                        FlowBus.post(CardUL(listOf(cardId)))
                    }
                }
            }
            delay(10)
            entry.getSendMessage()?.run { _eventFlow.emit(ConversationEvent.MessageReceived(this)) }
            delay(10)
        }
    }

    fun requestMediaContent(onComplete: () -> Unit) {
        viewModelScope.launchWithLoading {
            val mediaResponse = botService.getChatImage(mapOf("bot_id" to botId))
            UserManager.updateGems(mediaResponse.surplus)
            FlowBus.post(ChatChange())
            if (mediaResponse.unlock) {
                BotCacheService.updateBot(botId) {
                    cards.find { it.id == mediaResponse.cardId }?.isUnlock = true
                }
                FlowBus.post(CardUL(listOf(mediaResponse.cardId)))
            }
            ChatHelper.insertMessage(mediaResponse.apply { botId = this@ConversationViewModel.botId })
            mediaResponse.getReplyMessage()?.let { _eventFlow.emit(ConversationEvent.MessageReceived(it)) }
        }.onComplete {
            onComplete()
        }
    }


    fun removeMessageItem(message: BaseMessage) {
        viewModelScope.launchWithLoading {
            if (message.id >= 0) {
                botService.deleteMessage(
                    mapOf(
                        "chat_id" to message.id, 
                        "type" to if (message.isSend) 2 else 3
                    )
                )
            }
            if (message.isSend) {
                ChatHelper.deleteSendMessage(message.id)
            } else {
                ChatHelper.deleteReceivedMessage(message.id)
            }
            _eventFlow.emit(ConversationEvent.MessageDeleted(message))
        }
    }

    fun translateMessage(message: ReceivedTextMessage) {
        viewModelScope.launchSafety {
            message.translating = true
            _eventFlow.emit(ConversationEvent.MessageReceived(message))
            val translatedText: String = systemService.translate(
                mapOf(
                    "content" to message.message, 
                    "lang" to KVStore.speakLanguage.code
                )
            ).getOrDefault("text", "")
            message.translate = translatedText
            message.translating = false
            _eventFlow.emit(ConversationEvent.MessageReceived(message))
        }.onError {
            message.translating = false
            _eventFlow.tryEmit(ConversationEvent.MessageReceived(message))
        }
    }

    fun processContentAccess(message: ImageMessage) {
        viewModelScope.launchWithLoading {
            UnlockCardUseCase.execute(botId, message.cardId)
        }
    }

    fun retrySendMessage(sendMsg: SendTextMessage): Flow<ConversationStream.StreamStatus> {
        // 重置发送失败状态
        resetSendFailureStatus(sendMsg)
        // 发送加载消息
        sendLoadingMessage(sendMsg)

        val responseMsg = ReceivedTextMessage(-sendMsg.id)
        val streamHandler = ConversationStream()

        // 处理流状态
        handleStreamStatus(streamHandler, responseMsg, sendMsg)

        // 发起请求
        streamHandler.request(botId, sendMsg.message, chatMode)

        return streamHandler.sendFlow
    }

    private fun resetSendFailureStatus(sendMsg: SendTextMessage) {
        sendMsg.isSendFail = false
    }

    private fun sendLoadingMessage(sendMsg: SendTextMessage) {
        val processingIndicator = LoadingMessage(-sendMsg.id)
        _eventFlow.tryEmit(ConversationEvent.MessageReceived(processingIndicator))
    }

    private fun handleStreamStatus(
        streamHandler: ConversationStream,
        responseMsg: ReceivedTextMessage,
        sendMsg: SendTextMessage
    ) {
        streamHandler.sendFlow.onEach { streamStatus ->
            updateSendingState(streamStatus is ConversationStream.StreamStatus.Receiving ||
                    streamStatus is ConversationStream.StreamStatus.Start)
            handleStreamStatusInternal(streamStatus, responseMsg, sendMsg)
        }.launchIn(viewModelScope)
    }

    private fun handleStreamStatusInternal(
        streamStatus: ConversationStream.StreamStatus,
        responseMsg: ReceivedTextMessage,
        sendMsg: SendTextMessage
    ) {
        when (streamStatus) {
            ConversationStream.StreamStatus.End -> {
                handleMessageDelivered(responseMsg, sendMsg)
            }
            is ConversationStream.StreamStatus.Failed -> {
                val processingIndicator = LoadingMessage(-sendMsg.id)
                handleMessageDeliveryFailed(sendMsg, processingIndicator, streamStatus)
            }
            is ConversationStream.StreamStatus.Receiving -> {
                handleContentReceiving(responseMsg, streamStatus)
            }
            ConversationStream.StreamStatus.Start -> {
                // 开始发送，无需处理
            }
        }
    }

    fun deliverMessage(messageText: String): Flow<ConversationStream.StreamStatus> {
        val trimmedText = prepareMessageText(messageText)
        val (outgoingMessage, messageId) = createOutgoingMessage(trimmedText)
        sendMessageEvent(outgoingMessage)
        val processingIndicator = createLoadingMessage(messageId)
        sendMessageEvent(processingIndicator)
        val responseMsg = createResponseMessage(messageId)
        val streamHandler = ConversationStream()
        handleStreamStatus(streamHandler, responseMsg, outgoingMessage, processingIndicator)
        streamHandler.request(botId, trimmedText, chatMode)
        return streamHandler.sendFlow
    }

    private fun prepareMessageText(messageText: String): String {
        return messageText.trim()
    }

    private fun createOutgoingMessage(trimmedText: String): Pair<SendTextMessage, Int> {
        val messageId = abs(UUID.randomUUID().hashCode())
        return Pair(SendTextMessage(1 - messageId, message = trimmedText), messageId)
    }

    private fun sendMessageEvent(message: BaseMessage) {
        _eventFlow.tryEmit(ConversationEvent.MessageReceived(message))
    }

    private fun createLoadingMessage(messageId: Int): LoadingMessage {
        return LoadingMessage(-messageId)
    }

    private fun createResponseMessage(messageId: Int): ReceivedTextMessage {
        return ReceivedTextMessage(-messageId)
    }

    private fun handleStreamStatus(
        streamHandler: ConversationStream,
        responseMsg: ReceivedTextMessage,
        outgoingMessage: SendTextMessage,
        processingIndicator: LoadingMessage
    ) {
        streamHandler.sendFlow.onEach { streamStatus ->
            updateSendingState(streamStatus is ConversationStream.StreamStatus.Receiving ||
                    streamStatus is ConversationStream.StreamStatus.Start)
            handleStreamStatusInternal(streamStatus, responseMsg, outgoingMessage, processingIndicator)
        }.launchIn(viewModelScope)
    }

    private fun handleStreamStatusInternal(
        streamStatus: ConversationStream.StreamStatus,
        responseMsg: ReceivedTextMessage,
        outgoingMessage: SendTextMessage,
        processingIndicator: LoadingMessage
    ) {
        when (streamStatus) {
            ConversationStream.StreamStatus.End -> {
                handleMessageDelivered(responseMsg, outgoingMessage)
            }
            is ConversationStream.StreamStatus.Failed -> {
                handleMessageDeliveryFailed(outgoingMessage, processingIndicator, streamStatus)
            }
            is ConversationStream.StreamStatus.Receiving -> {
                handleContentReceiving(responseMsg, streamStatus)
            }
            ConversationStream.StreamStatus.Start -> {
                // 开始发送，无需处理
            }
        }
    }

    fun refreshMessageContent(message: ReceivedTextMessage): Flow<ConversationStream.StreamStatus> {
        val processingIndicator = LoadingMessage(message.id)
        _eventFlow.tryEmit(ConversationEvent.MessageReceived(processingIndicator))
        val originalContent: String = message.message
        message.message = ""
        message.translate = ""
        message.showEdit = false
        val streamHandler = ConversationStream()
        viewModelScope.launchSafety {
            streamHandler.sendFlow.collect { streamStatus ->
                updateSendingState(streamStatus is ConversationStream.StreamStatus.Receiving || 
                                streamStatus is ConversationStream.StreamStatus.Start)
                when (streamStatus) {
                    ConversationStream.StreamStatus.End -> {
                        handleMessageDelivered(message, null)
                        cancel()
                    }
                    is ConversationStream.StreamStatus.Failed -> {
                        handleRefreshFailed(message, originalContent, streamStatus)
                        cancel()
                    }
                    is ConversationStream.StreamStatus.Receiving -> {
                        handleContentReceiving(message, streamStatus)
                    }
                    ConversationStream.StreamStatus.Start -> {
                        // 开始发送，无需处理
                    }
                }
            }
        }
        streamHandler.request(message.id, "", chatMode, true)
        return streamHandler.sendFlow
    }


    private fun handleMessageDeliveryFailed(
        outgoingMsg: SendTextMessage?,
        processingIndicator: LoadingMessage,
        errorStatus: ConversationStream.StreamStatus.Failed
    ) {
        outgoingMsg?.isSendFail = true
        _eventFlow.tryEmit(ConversationEvent.MessageDeleted(processingIndicator))
        outgoingMsg?.let { _eventFlow.tryEmit(ConversationEvent.MessageReceived(it)) }
        val errorCode: Int = errorStatus.code
        if (errorCode == 0) {
            showToast(errorStatus.msg)
            return
        }
        if (errorCode == -1) {
            showToast(errorStatus.msg)
            netWorkChangeState.tryEmit(errorCode)
        }
    }

    private fun handleContentReceiving(
        responseMsg: ReceivedTextMessage, 
        receiveStatus: ConversationStream.StreamStatus.Receiving
    ) {
        if (responseMsg.message.isBlank()) {
            responseMsg.message += receiveStatus.content.trimStart()
        } else {
            responseMsg.message += receiveStatus.content
        }
        _eventFlow.tryEmit(ConversationEvent.MessageReceived(responseMsg))
    }


    private fun handleMessageDelivered(
        responseMsg: ReceivedTextMessage, 
        outgoingMsg: SendTextMessage?
    ) {
        syncConversationState(responseMsg, outgoingMsg)
        if (autoTranslate) {
            translateMessage(responseMsg)
        }
    }

    private fun handleRefreshFailed(
        message: ReceivedTextMessage, 
        originalContent: String, 
        errorStatus: ConversationStream.StreamStatus.Failed
    ) {
        message.message = originalContent
        _eventFlow.tryEmit(ConversationEvent.MessageReceived(message))
        val errorCode: Int = errorStatus.code
        if (errorCode == 0) {
            showToast(errorStatus.msg)
        }
        if (errorCode == -1) {
            showToast(errorStatus.msg)
            netWorkChangeState.tryEmit(errorCode)
        }
    }


    fun updateMessageContent(message: ReceivedTextMessage, updatedContent: String) {
        viewModelScope.launchWithLoading {
            userService.modifyChat(mapOf("chat_id" to message.id, "reply" to updatedContent))
            message.message = updatedContent
            ChatHelper.updateMessage(message.id) {
                reply1 = updatedContent
            }
            _eventFlow.emit(ConversationEvent.MessageReceived(message))
        }
    }

    fun updateFeatureStatus() {
        character.product = null
        _infoChangedFlow.tryEmit(character)
    }

    fun fetchRecommendations(onResult: (List<HomeListData>) -> Unit) {
        if (recommendList.isNotEmpty()) {
            onResult.invoke(recommendList)
        } else {
            viewModelScope.launchSafety {
                val recommendations: List<HomeListData> = botService.getHomeList(
                    mapOf(
                        "r_id" to null, 
                        "page" to 1, 
                        "size" to 12, 
                        "condition" to 0
                    )
                ).getOrDefault("list", emptyList())
                recommendList.clear()
                recommendList.addAll(recommendations)
                onResult.invoke(recommendList)
            }
        }
    }

    fun saveAgentToFavorites() {
        viewModelScope.launchSafety {
            collectRobotUseCase(botId)
        }
    }

    init {
        fetchAgentInfo()
        initializeSession()
        fetchFeatureInfo()
        fetchHistoryMessages()
    }
}